package org.gdstash.db;

import org.gdstash.file.ARCDecompress;
import org.gdstash.file.ARZRecord;
import org.gdstash.ui.GDStashFrame;
import org.gdstash.util.GDConstants;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.LinkedList;
import java.util.List;

public class DBEngineGame {
    private static final String TABLE_NAME = "GDC_GAME";
    private static final int ROW_ID = 1;
    private static final int ROW_FACTION_ALT_NEUTRAL_TAG = 2;
    private static final int ROW_FACTION_ALT_NEUTRAL_TEXT = 3;
    private static DBEngineGame singleton;
    private String factionAltNeutralTag;
    private String factionAltNeutralText;
    protected List<DBFactionReputation> reputations;
    protected List<DBEngineMasteryTier> masteryTiers;

    public DBEngineGame() {
        this.reputations = new LinkedList<DBFactionReputation>();
        this.masteryTiers = new LinkedList<DBEngineMasteryTier>();
    }

    private DBEngineGame(final ARZRecord record) {
        this.setFactionAltNeutralTag(record.getFactionAltNeutralTag());
        this.reputations = record.getFactionReputationList();
        this.masteryTiers = record.getMasteryTierList();
    }

    public static void createTable() throws SQLException {
        final String dropTable = "DROP TABLE GDC_GAME";
        final String createTable = "CREATE TABLE GDC_GAME (ID          VARCHAR(8) NOT NULL, FACTION_ALT_NEUTRAL_TAG  VARCHAR(64), FACTION_ALT_NEUTRAL_TEXT VARCHAR(64), PRIMARY KEY (ID))";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, "GDC_GAME")) {
                    st.execute(dropTable);
                }
                st.execute(createTable);
                st.close();
                conn.commit();
                DBFactionReputation.createTable(conn);
                DBEngineMasteryTier.createTable(conn);
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {"GDC_GAME"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
                GDMsgLogger.addError(msg);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static void insert(final ARZRecord record) throws SQLException {
        final DBEngineGame game = new DBEngineGame(record);
        DBEngineGame.singleton = null;
        final DBEngineGame entry = get();
        if (entry != null) {
            if (entry.getNumSkillTiers() > 0 && game.getNumSkillTiers() > 0) {
                return;
            }
            if (entry.getNumReputations() > 0 && game.getNumReputations() > 0) {
                return;
            }
        }
        final boolean factions = record.getFileName().equals("records/game/gamefactions.dbr");
        String sql = null;
        if (entry == null) {
            sql = "INSERT INTO GDC_GAME VALUES (?,?,?)";
        } else if (factions) {
            sql = "UPDATE GDC_GAME SET FACTION_ALT_NEUTRAL_TAG = ?, FACTION_ALT_NEUTRAL_TEXT = ? WHERE ID = ?";
        } else {
            sql = null;
        }
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            if (sql != null) {
                try (final PreparedStatement ps = conn.prepareStatement(sql)) {
                    if (entry == null) {
                        ps.setString(1, "DEFAULT");
                        ps.setString(2, game.getFactionAltNeutralTag());
                        ps.setString(3, game.getFactionAltNeutralText());
                    } else if (factions) {
                        ps.setString(1, game.getFactionAltNeutralTag());
                        ps.setString(2, game.getFactionAltNeutralText());
                        ps.setString(3, "DEFAULT");
                    }
                    ps.executeUpdate();
                    ps.close();
                    conn.commit();
                } catch (SQLException ex) {
                    conn.rollback();
                    GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_IN_GAME_CONFIG"));
                    GDMsgLogger.addError(ex);
                }
            }
            try {
                if (factions) {
                    for (final DBFactionReputation reputation : game.reputations) {
                        DBFactionReputation.insert(conn, record, reputation);
                    }
                } else {
                    for (final DBEngineMasteryTier tier : game.masteryTiers) {
                        DBEngineMasteryTier.insert(conn, record, tier);
                    }
                }
            } catch (SQLException ex) {
                conn.rollback();
                GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_IN_GAME_CONFIG"));
                GDMsgLogger.addError(ex);
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    private static List<DBEngineGame> wrap(final ResultSet rs) throws SQLException {
        final List<DBFactionReputation> reputations = DBFactionReputation.get();
        final List<DBEngineMasteryTier> masteryTiers = DBEngineMasteryTier.get();
        final LinkedList<DBEngineGame> list = new LinkedList<DBEngineGame>();
        while (rs.next()) {
            final DBEngineGame game = new DBEngineGame();
            game.factionAltNeutralTag = rs.getString(2);
            game.factionAltNeutralText = rs.getString(3);
            game.reputations = reputations;
            game.masteryTiers = masteryTiers;
            if (game.factionAltNeutralText != null) {
                final ByteBuffer buffer = StandardCharsets.UTF_8.encode(game.factionAltNeutralText);
                final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                game.factionAltNeutralText = utf8;
            }
            list.add(game);
        }
        return list;
    }

    public String getFactionAltNeutralTag() {
        return this.factionAltNeutralTag;
    }

    private void setFactionAltNeutralTag(final String factionAltNeutralTag) {
        this.factionAltNeutralTag = factionAltNeutralTag;
        if (factionAltNeutralTag == null) {
            this.factionAltNeutralText = null;
        } else {
            this.factionAltNeutralText = GDStashFrame.arcList.getTag(ARCDecompress.FileModule.All, GDConstants.TAG_TEXT_UI, factionAltNeutralTag, false);
        }
    }

    public String getFactionAltNeutralText() {
        return this.factionAltNeutralText;
    }

    public List<DBFactionReputation> getReputationList() {
        return this.reputations;
    }

    public int getMaxMasteryLevel() {
        if (this.masteryTiers == null) {
            return 0;
        }
        int level = 0;
        for (final DBEngineMasteryTier mt : this.masteryTiers) {
            if (mt.getMasteryLevel() > level) {
                level = mt.getMasteryLevel();
            }
        }
        return level;
    }

    public int getNumReputations() {
        if (this.reputations == null) {
            return 0;
        }
        return this.reputations.size();
    }

    public static void delete() throws SQLException {
        final String deleteEntry = "DELETE FROM GDC_GAME WHERE ID = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(deleteEntry)) {
                ps.setString(1, "DEFAULT");
                ps.executeUpdate();
                ps.close();
                DBFactionReputation.delete(conn);
                DBEngineMasteryTier.delete(conn);
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
        }
    }

    public int getNumSkillTiers() {
        if (this.masteryTiers == null) {
            return 0;
        }
        return this.masteryTiers.size();
    }

    public static DBEngineGame get() {
        if (DBEngineGame.singleton != null) {
            return DBEngineGame.singleton;
        }
        DBEngineGame.singleton = new DBEngineGame();
        final String command = "SELECT * FROM GDC_GAME WHERE ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, "DEFAULT");
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBEngineGame> list = wrap(rs);
                if (list.isEmpty()) {
                    DBEngineGame.singleton = null;
                } else {
                    DBEngineGame.singleton = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            GDMsgLogger.addError(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_READ_CONFIG_PLAYER"));
            GDMsgLogger.addError(ex2);
            DBEngineGame.singleton = null;
        }
        return DBEngineGame.singleton;
    }

    public DBEngineMasteryTier[] getSkillTierArray() {
        if (this.masteryTiers == null) {
            return null;
        }
        final DBEngineMasteryTier[] arr = new DBEngineMasteryTier[this.getNumSkillTiers()];
        int i = 0;
        for (final DBEngineMasteryTier mt : this.masteryTiers) {
            arr[i] = mt;
            ++i;
        }
        return arr;
    }

    public static void reset() {
        DBEngineGame.singleton = null;
    }
}
